﻿using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
using System.Text;
using up6.db.model;
using Up6.core.Up6Manager.Models;
using Up6.core.Up6Manager.Models.Req;

namespace up6.utils;

public class CryptoTool
{
    string key = "2C4DD1CC9KAX4TA9";
    string iv = "2C4DD1CC9KAX4TA9";

    /// <summary>
    /// aes-cbc-解密
    /// </summary>
    /// <param name="txt"></param>
    /// <param name="key"></param>
    /// <param name="iv"></param>
    /// <returns></returns>
    public string decode(string txt)
    {
        byte[] buf = Convert.FromBase64String(txt);

        int lenAlign = 0;//对齐长度
                         //16字节对齐
        if ((buf.Length % 16) > 0) lenAlign = 16 - buf.Length % 16;
        MemoryStream ms = new MemoryStream();
        //字节对齐
        ms.SetLength(buf.Length + lenAlign);
        ms.Write(buf, 0, buf.Length);

        byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
        byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);

        RijndaelManaged rDel = new RijndaelManaged();
        rDel.Key = keyArray;
        rDel.IV = ivArray;
        rDel.Mode = CipherMode.CBC;
        rDel.Padding = PaddingMode.Zeros;

        ICryptoTransform cTransform = rDel.CreateDecryptor();
        byte[] ori = cTransform.TransformFinalBlock(ms.ToArray(), 0, (int)ms.Length);

        return UTF8Encoding.UTF8.GetString(ori, 0, buf.Length).Trim();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="aligo"></param>
    /// <param name="s"></param>
    /// <param name="len">文件块原始长度</param>
    /// <returns></returns>
    public MemoryStream decode(string aligo, Stream s)
    {
        if (aligo.Equals("sm4")) return this.sm4_decode(s);
        else if (aligo.Equals("aes/ecb")) return this.aes_ecb_decode(s);
        return this.aes_decode(s);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="stm">加密时此大小是16字节对齐</param>
    /// <param name="lenOri">原始块长度</param>
    /// <returns></returns>

    public string sm4_encode(string s)
    {
        var data = Encoding.UTF8.GetBytes(s);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
        var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
        cipher.Init(true, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(data);

        return Convert.ToBase64String(outs);
    }
    public string sm4_ecb_encode(string s)
    {
        var data = Encoding.UTF8.GetBytes(s);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
        var cipher = CipherUtilities.GetCipher("SM4/ECB/PKCS5Padding");
        cipher.Init(true, new KeyParameter(keys));
        var outs = cipher.DoFinal(data);

        return Convert.ToBase64String(outs);
    }
    public string sm4_decode(string s)
    {
        var data = Convert.FromBase64String(s);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
        var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
        cipher.Init(false, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(data);

        return Encoding.UTF8.GetString(outs);
    }
    public MemoryStream sm4_encode(Stream stm, int lenOri)
    {
        var keys = UTF8Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
        var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
        cipher.Init(true, new ParametersWithIV(key, keys));

        MemoryStream bOut = new MemoryStream();
        CipherStream cOut = new CipherStream(bOut, null, cipher);
        stm.CopyTo(cOut);

        return bOut;
    }
    private MemoryStream sm4_decode(Stream s)
    {
        s.Seek(0, SeekOrigin.Begin);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
        var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
        cipher.Init(false, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(UtilsTool.toBytes(s));

        var ms = new System.IO.MemoryStream(outs);
        return ms;
    }
    public MemoryStream aes_decode(Stream s)
    {
        s.Seek(0, SeekOrigin.Begin);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("AES", keys);
        var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS5Padding");
        cipher.Init(false, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(UtilsTool.toBytes(s));

        var ms = new System.IO.MemoryStream(outs);
        return ms;
    }

    public string aes_cbc_pkcs(string s, bool base64 = true)
    {
        var data = Encoding.UTF8.GetBytes(s);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("AES", keys);
        var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS5Padding");
        cipher.Init(true, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(data);

        if (base64) return Convert.ToBase64String(outs);

        StringBuilder strbul = new StringBuilder();
        for (int i = 0; i < outs.Length; i++)
        {
            strbul.Append(outs[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位
        }
        return strbul.ToString().ToUpper();
    }

    public string aes_decode(string s)
    {
        var data = Convert.FromBase64String(s);
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("AES", keys);
        var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS5Padding");
        cipher.Init(false, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(data);

        return Encoding.UTF8.GetString(outs);
    }

    public MemoryStream aes_ecb_encode(Stream s)
    {
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("AES", keys);
        var cipher = CipherUtilities.GetCipher("AES/ECB/PKCS5Padding");
        cipher.Init(true, new KeyParameter(keys));
        var outs = cipher.DoFinal(UtilsTool.toBytes(s));

        var ms = new System.IO.MemoryStream(outs);
        return ms;
    }
    public MemoryStream aes_ecb_decode(Stream s)
    {
        var keys = Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("AES", keys);
        var cipher = CipherUtilities.GetCipher("AES/ECB/PKCS5Padding");
        cipher.Init(false, new KeyParameter(keys));
        var outs = cipher.DoFinal(UtilsTool.toBytes(s));

        var ms = new System.IO.MemoryStream(outs);
        return ms;
    }

    public MemoryStream sm4_decode(byte[] buf)
    {
        //s.Seek(0, SeekOrigin.Begin);
        var keys = UTF8Encoding.UTF8.GetBytes(this.key);
        var key = ParameterUtilities.CreateKeyParameter("SM4", keys);
        var cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
        cipher.Init(false, new ParametersWithIV(key, keys));
        var outs = cipher.DoFinal(buf);

        var ms = new System.IO.MemoryStream(outs);
        return ms;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="stm">加密时此大小是16字节对齐</param>
    /// <param name="lenOri">原始块长度</param>
    /// <returns></returns>
    public MemoryStream decode(Stream stm, int lenOri)
    {
        byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
        byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);

        RijndaelManaged rDel = new RijndaelManaged();
        rDel.Key = keyArray;
        rDel.IV = ivArray;
        rDel.Mode = CipherMode.CBC;
        rDel.Padding = PaddingMode.Zeros;

        MemoryStream sec = new MemoryStream();
        int lenAlign = 0;//对齐长度
                         //16字节对齐
        if ((stm.Length % 16) > 0) lenAlign = 16 - (int)stm.Length % 16;
        //块大小对齐
        sec.SetLength(stm.Length + lenAlign);
        stm.Seek(0, SeekOrigin.Begin);
        stm.CopyTo(sec);

        ICryptoTransform cTransform = rDel.CreateDecryptor();
        byte[] res = cTransform.TransformFinalBlock(sec.ToArray(), 0, (int)sec.Length);
        System.IO.MemoryStream ms = new System.IO.MemoryStream(res, 0, lenOri);
        return ms;
    }

    /// <summary>
    /// 完善逻辑，补齐文本长度，必须是16的倍数
    /// </summary>
    /// <param name="v"></param>
    /// <returns></returns>
    public string encode(string v)
    {
        byte[] src = Encoding.UTF8.GetBytes(v);
        int lenAlign = 0;//对齐长度
                         //16字节对齐
        if ((src.Length % 16) > 0) lenAlign = 16 - src.Length % 16;
        MemoryStream ms = new MemoryStream();
        ms.SetLength(src.Length + lenAlign);
        ms.Write(src, 0, src.Length);

        byte[] keyArray = Encoding.UTF8.GetBytes(key);
        byte[] ivArray = Encoding.UTF8.GetBytes(iv);

        RijndaelManaged rDel = new RijndaelManaged();
        rDel.Key = keyArray;
        rDel.IV = ivArray;
        rDel.Mode = CipherMode.CBC;
        rDel.Padding = PaddingMode.Zeros;

        ICryptoTransform tf = rDel.CreateEncryptor();
        byte[] resultArray = tf.TransformFinalBlock(ms.ToArray(), 0, (int)ms.Length);

        return Convert.ToBase64String(resultArray, Base64FormattingOptions.None);
    }

    /// <summary>
    /// 计算token，进行授权检查
    /// token = encode( md5(id+nameLoc))
    /// </summary>
    /// <param name="f"></param>
    /// <param name="action">动作：init,block,cmp</param>
    /// <returns></returns>
    public string token(FileInf f, string action = "init")
    {
        string str = f.id + f.nameLoc + action;
        if (action == "block") str = f.id + f.pathSvr + action;
        str = this.md5(str);
        str = this.encode(str);
        return str;
    }

    public string md5(string s)
    {
        byte[] sor = Encoding.UTF8.GetBytes(s);
        MD5 md5 = MD5.Create();
        byte[] result = md5.ComputeHash(sor);
        StringBuilder strbul = new StringBuilder(40);
        for (int i = 0; i < result.Length; i++)
        {
            strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位

        }
        return strbul.ToString();
    }
}
